package sf.database.jdbc.sql;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sf.database.jdbc.rowmapper.RowMapper;

import java.io.Closeable;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * 实现结果集的迭代接口.
 * @param <T>
 * @author sxf
 */
public class ResultSetIteration<T> implements Iterator<T>, Iterable<T>, Closeable {
    private static final Logger logger = LoggerFactory.getLogger(ResultSetIteration.class);
    protected ResultSet rs;
    protected ResultSetMetaData rsmd;
    protected RowMapper<T> rm;
    /**
     * 处理的记录位置(第几条记录)：可以只针对某一条记录做特殊处理,从0开始
     */
    protected int rowNum;

    private T object;

    public ResultSetIteration() {

    }

    public ResultSetIteration(ResultSet rs, RowMapper<T> rm) {
        this.rs = rs;
        this.rm = rm;
        if (rs != null) {
            try {
                this.rsmd = rs.getMetaData();
            } catch (SQLException e) {
                rethrow(e);
            }
        }
    }

    /**
     * 重置
     */
    public void reset() {
        rowNum = 0;
        object = null;
        rs = null;
        rsmd = null;
    }

    protected T fetchNext() {
        T result = null;
        try {
            if (rs.next()) {
                result = rm.handle(rs, rsmd, rowNum++);
            }
        } catch (SQLException e) {
            rethrow(e);
        }
        return result;
    }


    /**
     * Iterator接口
     * @return
     */
    @Override
    public boolean hasNext() {
        //!rs.isLast();
        if (object == null) {
            object = fetchNext();
        }
        return object != null;
    }

    /**
     * Iterator接口
     * @return
     */
    @Override
    public T next() {
        // Fill next with object fetched from hasNext()
        T next = object;

        if (next == null) {
            next = fetchNext();
        }

        if (next != null) {
            object = null;
            return next;
        }
        throw new NoSuchElementException();
    }

    public void remove() {
        try {
            this.rs.deleteRow();
        } catch (SQLException e) {
            rethrow(e);
        }
    }

    @Override
    public void close() {
        if (rs != null) {
            try {
                Statement statement = rs.getStatement();
                rs.close();
                if (statement != null) {
                    statement.close();
                }
            } catch (SQLException e) {
                logger.error("", e);
            }
        }
        rs = null;
    }

    /**
     * Iterable接口
     * @return
     */
    @Override
    public Iterator<T> iterator() {
        return this;
    }

    protected void rethrow(SQLException e) {
        throw new RuntimeException(e);
    }

    public ResultSet getRs() {
        return rs;
    }

    public RowMapper<T> getRm() {
        return rm;
    }

    public Stream<T> stream() {
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, 0), false);
    }

    /**
     * Generates an <code>Iterable</code>, suitable for use in for-each loops.
     * @param rs Wrap this <code>ResultSet</code> in an <code>Iterator</code>.
     * @return an <code>Iterable</code>, suitable for use in for-each loops.
     */
    public static <T> ResultSetIteration<T> iteration(ResultSet rs, RowMapper<T> mapper) {
        return new ResultSetIteration<>(rs, mapper);
    }
}
